Helper.using('py.Actions', function (ns) {
    ns.Action = function () {
        var action = this;
        EventTarget.call(this);

        this.isDone = false;

        this.invokeFinish = function (data) {
            data = data || {};
            data.type = 'finished';
            action.fire(data);
        }

        this.stop = Helper.abstractMethod();

        this.start = function () {
            this.isDone = false;
            action.fire('started');
            this.startFunc();
        };

        this.startFunc = Helper.abstractMethod();

        this.stopPreview = function () {
        }; // sẽ chuyển thành abstract

        return this;
    };

    ns.ParallelAction = function (params) {
        ns.Action.call(this, params);

        var self = this,
            actions = params.actions || [],
            finish, preview;


        finish = function (event) {
            console.log(this, event);
            self.stop();
            self.invokeFinish(event);
        };

        preview = function (event) {
            actions.forEach(function (action) {
                if (event.target !== action) {
                    action.stopPreview();
                }
            });
            event.originalTarget = event.target;
            event.target = this;
            self.fire(event);
        };


        this.startFunc = function () {
            py.log('started', self, self.isDone);
            actions.forEach(function (action) {
                action.on('finished', finish);
                action.on('preview', preview);
                action.start();
            })
        }

        this.stop = function () {
            actions.forEach(function (action) {
                action.off('finished', finish);
                action.off('preview', preview);
                action.stop();
            });
            py.log('stopped', self, self.isDone);
        };


        return this;
    };

    ns.SelectAction = function (drawingPad, params) {
        ns.Action.call(this, drawingPad);

        var action = this, selector;

        params = Helper.extend({
            selectable:function () {
                return true;
            },
            stop:function (selected) {
                return s.length == 1;
            }
        }, params);

        function hover(figure, on) {
            var visual = figure.visual(),
                layer = visual.getLayer();
            if (on) {
                figure.addShadow();
                action.invokePreview({figure:figure});
            } else {
                figure.removeShadow();
            }
            layer.draw();
        }

        this.invokePreview = function (data) {
            data = data || {};
            data.type = 'preview';
            this.fire(data);
        };

        this.start = function () {
            this.fire('started');

            selector = drawingPad.getSelector(params.selectable);
            selector.on('mouseover',
                function () {
                    hover(this, true);
                }).on('mouseout',
                function () {
                    hover(this, false);
                }).on('click',
                function () {
                    hover(this, false);
                    action.invokeFinish({selected:[this]})
                }).start();
        };

        this.stop = function () {
            selector && selector.stop && selector.stop();
        }
    };

    ns.SelectAction.byType = function (drawingPad, type) {
        if (type === 'Point') {
            return new ns.DrawPoint(drawingPad);
        }
        return new ns.SelectAction(drawingPad, {
            selectable:'is' + type
        });
    };

    ns.DrawAction = function (drawingPad) {
        ns.Action.apply(this, [drawingPad]);

        var action = this,
            _previewLayer = drawingPad.previewLayer(),
            _super = {
                invokeFinish:this.invokeFinish,
                start:this.start
            };


        this.stop = function () {
            if (action.isDone) {
                return;
            }
            py.log('stopped', action, action.isDone);
            _previewLayer.clear();
            _previewLayer.removeChildren();
            action.isDone = true;
        };

        /*
         @override invokeFinish
         */
        this.invokeFinish = function (event) {
            this.stop();
            this.isDone = true;
            if (event.figure) {
                drawingPad.figures.push(event.figure);
            }
            _super.invokeFinish.apply(this, arguments);
        };

        this.invokePreview = function (data) {
            data = data || {};
            data.type = 'preview';
            this.fire(data);
        };

        this.stopPreview = function () {
            _previewLayer.clear();
        };

        this.previewLayer = function () {
            return _previewLayer;
        };

        // abstract method
        this.startFunc = Helper.abstractMethod();

        return this;
    };

    ns.MoveAction = function (drawingPad) {
        ns.Action.call(this);

        var action = this,
            selector,
            movingFigure;

        function hover(figure, on) {
            var visual = figure.visual(),
                layer = visual.getLayer();
            if (on) {
                figure.addShadow();
            } else {
                figure.removeShadow();
            }

            layer.draw();
        }


        this.start = function () {
            selector = drawingPad.getSelector('isMovable');
            selector.each(function (figure) {
                figure.startMove();
            });

            function onDragStart() {
                var visual = this.visual();
                hover(this, false);
                movingFigure = this;

                visual.on('dragmove', function () {
                    console.log(this, arguments);
                });

                visual.on('dragend', function () {
                    visual.off('dragmove dragend');
                    action.stop();
                    console.log(this, arguments);
                });

                selector.stop();
            }

            selector.on('mouseover',
                function () {
                    hover(this, true);
                    this.startMove();
                }).on('mouseout',
                function () {
                    hover(this, false);
                    this.stopMove();
                }).on('dragstart', onDragStart)
                .start();
        };

        this.stop = function () {
            movingFigure && movingFigure.stopMove();
            selector.stop();
        };


    };
});
